import time
import threading
from pymodbus.client import ModbusTcpClient
from pymodbus.transaction import ModbusRtuFramer  # 重要：导入 RTU 帧处理器
from pymodbus.payload import BinaryPayloadDecoder
from pymodbus.constants import Endian
from pydispatch import dispatcher

from logger import log
from settings import settings


def decode_registers_data(registers_slice, data_type, byte_word_order_code):
    """
    根据给定的类型和字节/字顺序解码单个数据项的寄存器数据。
    解码方式说明:
    - "1234" (32位): wordorder=Endian.Big, byteorder=Endian.Big
    - "12" (16位): byteorder=Endian.Big (wordorder不适用)
    如需其他顺序，请修改此处的 wo 和 bo 变量。
    """
    wo = Endian.BIG
    bo = Endian.BIG

    if data_type == "float":
        if byte_word_order_code == "1234":  # ABCD
            wo = Endian.BIG
            bo = Endian.BIG
        # ... (其他字节序/字序的elif分支可以加在这里) ...
        else:
            log.info(f"警告: 未知的32位解码方式 '{byte_word_order_code}' for float, 使用默认 BIG")
        decoder = BinaryPayloadDecoder.fromRegisters(registers_slice, byteorder=bo, wordorder=wo)
        return round(decoder.decode_32bit_float(), 2)

    elif data_type == "uint":
        if byte_word_order_code == "12":  # AB
            bo = Endian.BIG
        # ... (其他字节序的elif分支可以加在这里) ...
        else:
            log.info(f"警告: 未知的16位解码方式 '{byte_word_order_code}' for uint, 使用默认 Big")
        decoder = BinaryPayloadDecoder.fromRegisters(registers_slice, byteorder=bo, wordorder=Endian.BIG)
        return decoder.decode_16bit_uint()
    else:
        log.info(f"错误: 未知数据类型 '{data_type}'")
        return "未知数据类型"


def read_device_data(device_config):
    name = device_config["name"]
    ip = device_config["ip"]
    port = device_config["port"]
    slave_id = device_config["slave_id"]
    read_interval = device_config["read_interval"]
    block_definitions = device_config["block_definitions"]

    # 重要: 使用 ModbusRtuFramer
    client = ModbusTcpClient(ip, port=port, framer=ModbusRtuFramer)

    log.info(
        f"[{name}] 开始读取 Modbus RTU over TCP/IP 数据从 {ip}:{port}, RTU从站ID: {slave_id}, 间隔: {read_interval}s")

    while True:
        try:
            if not client.is_socket_open():
                log.info(f"[{name}] 尝试连接到 Modbus 服务器 (RTU over TCP)...")
                if not client.connect():  # connect() 返回 True 表示连接成功
                    log.info(f"[{name}] 连接失败，将在 {read_interval} 秒后重试...")
                    time.sleep(read_interval)
                    continue
                log.info(f"[{name}] 连接成功!")

            for block_def in block_definitions:
                base_name = block_def["base_name"]
                start_addr_1based = block_def["start_address"]
                num_items = block_def["num_items"]
                regs_per_item = block_def["registers_per_item"]
                data_type = block_def["data_type"]
                decode_code = block_def["decode_code"]

                total_registers_for_block = num_items * regs_per_item
                block_start_addr_0based = start_addr_1based - 1

                try:
                    rr = client.read_holding_registers(address=block_start_addr_0based,
                                                       count=total_registers_for_block,
                                                       slave=slave_id)  # 在 RTU 模式下，slave 参数是必须的

                    if rr.isError():
                        log.error(f"[{name}] 错误: 读取数据块 '{base_name}' 失败: {rr}")
                        continue

                    raw_block = rr.registers

                    current_reg_offset = 0
                    data_list = []
                    for i in range(num_items):
                        item_registers_slice = raw_block[current_reg_offset: current_reg_offset + regs_per_item]
                        value = decode_registers_data(item_registers_slice, data_type, decode_code)
                        # 防止数字太大，出现FF的问题
                        if value > 50000:
                            value = 0
                        # log.info(f"  [{name}] {item_name}: {value}")
                        data_list.append(value)
                        current_reg_offset += regs_per_item
                    log.info(f"[{name}] {base_name}: {data_list}")
                    dispatcher.send("modbus", name=name, base_name=base_name, data_list=data_list)
                except Exception as e:
                    log.error(f"[{name}] 解析或读取块 '{base_name}' 时发生一般错误: {e}")
            time.sleep(read_interval)
        except Exception as e:
            log.error(f"[{name}] 线程发生未处理异常: {e}")
            if client.is_socket_open():
                client.close()
            time.sleep(read_interval)


def main():
    threads = []
    log.info("启动 Modbus RTU over TCP/IP 读取线程...")

    for config in settings.modbus_list:
        thread = threading.Thread(target=read_device_data, args=(config,), daemon=True)
        threads.append(thread)
        thread.start()
        log.info(f"线程 for {config['name']} 已启动.")


if __name__ == "__main__":
    main()
